package com.robotic.sensor.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.robotic.sensor.domain.RegionBoundary;
import com.robotic.sensor.mapper.RegionBoundaryMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.List;

/**
 * 基于坐标的区域判断服务
 * 
 * @author robotic
 * @date 2024-01-20
 */
@Slf4j
@Service
public class CoordinateRegionService {

    @Autowired
    private RegionBoundaryMapper regionBoundaryMapper;

    /**
     * 根据坐标判断机器人所在的区域
     *
     * @param x X坐标
     * @param y Y坐标
     * @param z Z坐标
     * @return 区域代码，如果未找到匹配区域则返回null
     */
    public String getRegionByCoordinate(Double x, Double y, Double z) {
        if (x == null || y == null) {
            log.warn("坐标参数不能为空: x={}, y={}, z={}", x, y, z);
            return null;
        }
        
        BigDecimal xBd = BigDecimal.valueOf(x);
        BigDecimal yBd = BigDecimal.valueOf(y);
        BigDecimal zBd = z != null ? BigDecimal.valueOf(z) : BigDecimal.ZERO;
        
        return getRegionByCoordinate(xBd, yBd, zBd);
    }

    /**
     * 根据坐标判断机器人所在的区域
     *
     * @param x X坐标
     * @param y Y坐标
     * @param z Z坐标
     * @return 区域代码，如果未找到匹配区域则返回null
     */
    public String getRegionByCoordinate(BigDecimal x, BigDecimal y, BigDecimal z) {
        try {
            log.debug("开始根据坐标({}, {}, {})判断所在区域", x, y, z);
            
            // 获取所有启用的区域边界配置，按优先级排序
            List<RegionBoundary> regionBoundaries = regionBoundaryMapper.selectActiveRegionBoundariesOrderByPriority();
            
            if (regionBoundaries == null || regionBoundaries.isEmpty()) {
                log.warn("未找到任何启用的区域边界配置");
                return null;
            }
            
            // 遍历所有区域边界，找到第一个包含该坐标的区域
            for (RegionBoundary boundary : regionBoundaries) {
                if (isPointInRegion(x, y, z, boundary)) {
                    log.info("坐标({}, {}, {})位于区域: {} ({})", x, y, z, boundary.getRegionCode(), boundary.getRegionName());
                    return boundary.getRegionCode();
                }
            }
            
            log.debug("坐标({}, {}, {})不在任何已定义的区域内", x, y, z);
            return null;
            
        } catch (Exception e) {
            log.error("根据坐标判断区域时发生异常: {}", e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据坐标字符串判断机器人所在的区域
     *
     * @param xStr X坐标字符串
     * @param yStr Y坐标字符串
     * @param zStr Z坐标字符串
     * @return 区域代码，如果不在任何区域内则返回null
     */
    public String getRegionByCoordinate(String xStr, String yStr, String zStr) {
        try {
            if (!StringUtils.hasText(xStr) || !StringUtils.hasText(yStr)) {
                log.warn("坐标参数不完整: x={}, y={}, z={}", xStr, yStr, zStr);
                return null;
            }
            
            BigDecimal x = new BigDecimal(xStr);
            BigDecimal y = new BigDecimal(yStr);
            BigDecimal z = StringUtils.hasText(zStr) ? new BigDecimal(zStr) : BigDecimal.ZERO;
            
            return getRegionByCoordinate(x, y, z);
            
        } catch (NumberFormatException e) {
            log.error("坐标格式错误: x={}, y={}, z={}, 错误: {}", xStr, yStr, zStr, e.getMessage());
            return null;
        }
    }

    /**
     * 判断指定坐标是否在区域内
     *
     * @param x X坐标
     * @param y Y坐标
     * @param z Z坐标
     * @param boundary 区域边界定义
     * @return true-在区域内，false-不在区域内
     */
    private boolean isPointInRegion(BigDecimal x, BigDecimal y, BigDecimal z, RegionBoundary boundary) {
        try {
            // 首先进行边界框快速筛选
            if (!boundary.isInBoundingBox(x, y, z)) {
                return false;
            }
            
            // 根据边界类型进行精确判断
            String boundaryType = boundary.getBoundaryType();
            String boundaryData = boundary.getBoundaryData();
            
            if (!StringUtils.hasText(boundaryData)) {
                log.warn("区域 {} 的边界数据为空", boundary.getRegionCode());
                return false;
            }
            
            switch (boundaryType) {
                case "rectangle":
                    return isPointInRectangle(x, y, z, boundaryData);
                case "polygon":
                    return isPointInPolygon(x, y, z, boundaryData);
                case "circle":
                    return isPointInCircle(x, y, z, boundaryData);
                default:
                    log.warn("不支持的边界类型: {}", boundaryType);
                    return false;
            }
            
        } catch (Exception e) {
            log.error("判断坐标是否在区域 {} 内时发生异常: {}", boundary.getRegionCode(), e.getMessage(), e);
            return false;
        }
    }

    /**
     * 判断点是否在矩形内
     * 矩形数据格式: {"minX": 0, "maxX": 100, "minY": 0, "maxY": 100, "minZ": 0, "maxZ": 10}
     */
    private boolean isPointInRectangle(BigDecimal x, BigDecimal y, BigDecimal z, String boundaryData) {
        try {
            JSONObject rect = JSON.parseObject(boundaryData);
            
            BigDecimal minX = rect.getBigDecimal("minX");
            BigDecimal maxX = rect.getBigDecimal("maxX");
            BigDecimal minY = rect.getBigDecimal("minY");
            BigDecimal maxY = rect.getBigDecimal("maxY");
            BigDecimal minZ = rect.getBigDecimal("minZ");
            BigDecimal maxZ = rect.getBigDecimal("maxZ");
            
            return (minX == null || x.compareTo(minX) >= 0) &&
                   (maxX == null || x.compareTo(maxX) <= 0) &&
                   (minY == null || y.compareTo(minY) >= 0) &&
                   (maxY == null || y.compareTo(maxY) <= 0) &&
                   (minZ == null || z.compareTo(minZ) >= 0) &&
                   (maxZ == null || z.compareTo(maxZ) <= 0);
                   
        } catch (Exception e) {
            log.error("解析矩形边界数据失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 判断点是否在多边形内(使用射线法)
     * 多边形数据格式: {"points": [{"x": 0, "y": 0}, {"x": 100, "y": 0}, {"x": 100, "y": 100}, {"x": 0, "y": 100}], "minZ": 0, "maxZ": 10}
     */
    private boolean isPointInPolygon(BigDecimal x, BigDecimal y, BigDecimal z, String boundaryData) {
        try {
            JSONObject polygon = JSON.parseObject(boundaryData);
            JSONArray points = polygon.getJSONArray("points");
            BigDecimal minZ = polygon.getBigDecimal("minZ");
            BigDecimal maxZ = polygon.getBigDecimal("maxZ");
            
            // 检查Z坐标范围
            if ((minZ != null && z.compareTo(minZ) < 0) || (maxZ != null && z.compareTo(maxZ) > 0)) {
                return false;
            }
            
            // 使用射线法判断点是否在多边形内
            int intersections = 0;
            int pointCount = points.size();
            
            for (int i = 0; i < pointCount; i++) {
                JSONObject p1 = points.getJSONObject(i);
                JSONObject p2 = points.getJSONObject((i + 1) % pointCount);
                
                BigDecimal x1 = p1.getBigDecimal("x");
                BigDecimal y1 = p1.getBigDecimal("y");
                BigDecimal x2 = p2.getBigDecimal("x");
                BigDecimal y2 = p2.getBigDecimal("y");
                
                // 检查射线是否与边相交
                if (isRayIntersectSegment(x, y, x1, y1, x2, y2)) {
                    intersections++;
                }
            }
            
            // 奇数次相交表示在多边形内
            return intersections % 2 == 1;
            
        } catch (Exception e) {
            log.error("解析多边形边界数据失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 判断点是否在圆形内
     * 圆形数据格式: {"centerX": 50, "centerY": 50, "radius": 25, "minZ": 0, "maxZ": 10}
     */
    private boolean isPointInCircle(BigDecimal x, BigDecimal y, BigDecimal z, String boundaryData) {
        try {
            JSONObject circle = JSON.parseObject(boundaryData);
            
            BigDecimal centerX = circle.getBigDecimal("centerX");
            BigDecimal centerY = circle.getBigDecimal("centerY");
            BigDecimal radius = circle.getBigDecimal("radius");
            BigDecimal minZ = circle.getBigDecimal("minZ");
            BigDecimal maxZ = circle.getBigDecimal("maxZ");
            
            // 检查Z坐标范围
            if ((minZ != null && z.compareTo(minZ) < 0) || (maxZ != null && z.compareTo(maxZ) > 0)) {
                return false;
            }
            
            // 计算到圆心的距离
            BigDecimal dx = x.subtract(centerX);
            BigDecimal dy = y.subtract(centerY);
            BigDecimal distanceSquared = dx.multiply(dx).add(dy.multiply(dy));
            BigDecimal radiusSquared = radius.multiply(radius);
            
            return distanceSquared.compareTo(radiusSquared) <= 0;
            
        } catch (Exception e) {
            log.error("解析圆形边界数据失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 判断从点发出的水平射线是否与线段相交
     */
    private boolean isRayIntersectSegment(BigDecimal px, BigDecimal py, BigDecimal x1, BigDecimal y1, BigDecimal x2, BigDecimal y2) {
        // 确保y1 <= y2
        if (y1.compareTo(y2) > 0) {
            BigDecimal temp;
            temp = x1; x1 = x2; x2 = temp;
            temp = y1; y1 = y2; y2 = temp;
        }
        
        // 射线在线段的Y范围外
        if (py.compareTo(y1) < 0 || py.compareTo(y2) >= 0) {
            return false;
        }
        
        // 线段是水平的
        if (y1.compareTo(y2) == 0) {
            return false;
        }
        
        // 计算交点的X坐标
        BigDecimal intersectionX = x1.add(
            py.subtract(y1).multiply(x2.subtract(x1)).divide(y2.subtract(y1), 10, BigDecimal.ROUND_HALF_UP)
        );
        
        // 射线向右，交点必须在点的右侧
        return intersectionX.compareTo(px) > 0;
    }
}